package org.hawk.gwt.ppc.parser.impl;

import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.lookupNext;

import java.util.LinkedList;
import java.util.List;

import org.hawk.gwt.ppc.parser.AssertKeywordExpressionDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.parser.DescriptorVisitor;
import org.hawk.gwt.ppc.parser.ExpressionDescriptor;
import org.hawk.gwt.ppc.parser.JavaKeywords;
import org.hawk.gwt.ppc.parser.KeywordExpressionDescriptor;
import org.hawk.gwt.ppc.parser.Token;
import org.hawk.gwt.ppc.utils.PreprocessorException;

/**
 * Keyword expression is the line expression that begins with java keyword (e.g. return).
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class KeywordExpressionDescriptorParser implements DescriptorParser {

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#accepts(org.hawk.gwt.ppc.parser.Token)
	 */
	public boolean accepts(Token token) {
		return token.equalsTo(JavaKeywords.RETURN) ||
				token.equalsTo(JavaKeywords.BREAK) ||
				token.equalsTo(JavaKeywords.CONTINUE) ||
				token.equalsTo(JavaKeywords.THROW) ||
				token.equalsTo(JavaKeywords.ASSERT);
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#parse(org.hawk.gwt.ppc.parser.impl.TokenBasedJavaParser, org.hawk.gwt.ppc.parser.impl.Tokenizer, java.lang.Object[])
	 */
	public Descriptor parse(TokenBasedJavaParser parser, TokenSource tokenizer,
			Object... params) throws PreprocessorException {
		Token first = tokenizer.current();
		String type = first.toString();
		lookupNext(tokenizer, false);
		
		if (first.equalsTo(JavaKeywords.ASSERT)) {
			return parseAssert(first, type, parser, tokenizer);
		}
		
		Descriptor expression = null;
		Token last;
		
		if (!tokenizer.current().equalsTo(';')) {
			expression = DescriptorType.LINE_EXPRESSION.parse(parser, tokenizer);
			last = ((AbstractDescriptor)expression).getLastToken();
		} else {
			last = tokenizer.current();
			lookupNext(tokenizer, false);
		}
		return new KeywordExpression(first, last, type, expression);
	}
	
	private Descriptor parseAssert(Token first, String type,
			TokenBasedJavaParser parser, TokenSource tokenizer) throws PreprocessorException {
		LinkedList<Token> condition = new LinkedList<Token>();
		List<Token> message = new LinkedList<Token>();
		int deep = 1;
		boolean noMessage = false;
		while (deep > 0) {
			condition.add(tokenizer.current());
			if (tokenizer.current().equalsTo('?') ||
					tokenizer.current().equalsTo('(') ||
					tokenizer.current().equalsTo('{')) {
				deep++;
			} else if (tokenizer.current().equalsTo(':') ||
					tokenizer.current().equalsTo(')') ||
					tokenizer.current().equalsTo('}')) {
				deep--;
			}
			if (deep == 1 && tokenizer.current().equalsTo(';')) {
				noMessage = true;
				break;
			}
			lookupNext(tokenizer, false);
		}
		condition.removeLast();
		
		if (!noMessage) {
			while (!tokenizer.current().equalsTo(';')) {
				message.add(tokenizer.current());
				lookupNext(tokenizer, false);
			}
		}
		
		TokenSource conditionIterator = new IteratorTokenSource(condition.iterator());
		conditionIterator.next();
		Descriptor conditionExpression = DescriptorType.LINE_EXPRESSION.parse(parser, conditionIterator, true);
		
		Descriptor messageExpression = null;
		if (!noMessage) {
			TokenSource messageIterator = new IteratorTokenSource(message.iterator());
			messageIterator.next();
			messageExpression = DescriptorType.LINE_EXPRESSION.parse(parser, messageIterator, true);
		}
		
		Token last = tokenizer.current();
		lookupNext(tokenizer, false);
		return new AssertExpression(first, last, JavaKeywords.ASSERT, conditionExpression, messageExpression);
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private static class KeywordExpression extends AbstractExpressionDescriptor implements KeywordExpressionDescriptor {
		
		private String keyword;
		private ExpressionDescriptor argument;
		
		KeywordExpression(Token first, Token last, String keyword, Descriptor argument) {
			super(first, last);
			
			this.keyword = keyword;
			this.argument = (ExpressionDescriptor)argument;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.KeywordExpressionDescriptor#getKeyword()
		 */
		public String getKeyword() {
			return keyword;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.KeywordExpressionDescriptor#getArgument()
		 */
		public ExpressionDescriptor getArgument() {
			return argument;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			if (argument != null) {
				argument.visit(visitor);
			}
		}
	}
	
	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	private static class AssertExpression extends KeywordExpression implements AssertKeywordExpressionDescriptor {
		
		private ExpressionDescriptor message;

		AssertExpression(Token first, Token last, String keyword, Descriptor argument, Descriptor message) {
			super(first, last, keyword, argument);
			this.message = (ExpressionDescriptor)message;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.AssertKeywordExpressionDescriptor#getMessage()
		 */
		public ExpressionDescriptor getMessage() {
			return message;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.impl.KeywordExpressionDescriptorParser.KeywordExpression#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		@Override
		public void visit(DescriptorVisitor visitor)
				throws PreprocessorException {
			super.visit(visitor);
			
			if (message != null) {
				message.visit(visitor);
			}
		}
		
	}
	
}
